//
// RS Game Framework
// Copyright © 2009 Jedd Haberstro
// jhaberstro@gmail.com
// 
// $Id:
//

#include "rs/Mutex.hpp"
#include "rs/Assert.hpp"
#include "rs/portability/platforms/posix/Implementations.hpp"
#include <algorithm>
#include <errno.h>
#include <pthread.h>

namespace rs
{
    Mutex::Mutex()
    : implementation_(new internal::MutexImplementation),
      locked_(false) {
        pthread_mutexattr_t attribs;
        int err = pthread_mutexattr_init(&attribs);
        RS_ASSERTF(err == 0, "Could not initialize pthread mutex attributes with error code: %i", err);
        
#       if defined(RS_DEBUG)
        err = pthread_mutexattr_settype(&attribs, PTHREAD_MUTEX_ERRORCHECK);
        RS_ASSERTF(err == 0, "Could not set pthread mutex attributes with error code: %i", err);
#       endif
        
        err = pthread_mutex_init(&implementation_->mutexHandle_, &attribs);
        RS_ASSERTF(err == 0, "Could not initialize pthread mutex with error code: %i", err);
        
        err = pthread_mutexattr_destroy(&attribs);
        RS_ASSERTF(err == 0, "Could not destroy pthread mutex attributes with error code: %i", err);
    }
    
    Mutex::Mutex(Mutex const& other)
    : implementation_(other.implementation_),
      locked_(other.locked_) {
    }
    
    Mutex::~Mutex() {
        int err = pthread_mutex_destroy(&implementation_->mutexHandle_);
        RS_ASSERTF(err == 0, "Could not destroy pthread mutex with error code: %i", err);
    }
    
    void Mutex::Lock() {
        int err = pthread_mutex_lock(&implementation_->mutexHandle_);
        RS_ASSERTF(err == 0, "Could not lock pthread mutex with error code: %i", err);
        locked_ = true;
    }
    
    Boolean Mutex::TryLock() {
        int err = pthread_mutex_trylock(&implementation_->mutexHandle_);
        RS_ASSERTF(err != EINVAL, "Could not try-lock pthread mutex with error code: %i", err);
        locked_ = (err == 0);
        return locked_;
    }
    
    void Mutex::Unlock() {
        int err = pthread_mutex_unlock(&implementation_->mutexHandle_);
        RS_ASSERTF(err == 0, "Could not unlock pthread mutex with error code: %i", err);
        locked_ = false;
    }
    
    void Mutex::Swap(Mutex& other) {
        std::swap(implementation_, other.implementation_);
        std::swap(locked_, other.locked_);
    }
    
    Mutex& Mutex::operator=(Mutex const& other) {
        if (this != &other) {
            Mutex(other).Swap(*this);
        }
        
        return *this;
    }
}